﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Data.GameData.Entities;
using Data.GameData.Utils;

namespace Data.GameData
{
    public class SystemBody : MapObject, ICloneable
    {
        private ESystemBody dbSystemBody;
        private static Dictionary<Guid, SystemBody> systemBodyList = null;

        private Guid starSystemId;
        private SystemBodyType type;
        private SystemBodyEnviroment enviroment;
        private StarType starType;
        private int mass;
        private double rei;
        private int habRating;

        private int radius;
        private int initialBearing;

        private List<SystemBody> satalites = new List<SystemBody>();
        private SystemBody orbits;

        private Population population;

        #region Constructors
        public SystemBody(Guid systemId,
                          SystemBodyType type,
                          SystemBodyEnviroment enviroment,
                          int mass,
                          int radius,
                          SystemBody orbitsAround)
        {
            dbSystemBody = new ESystemBody();

            this.starSystemId = systemId;
            this.type = type;
            this.enviroment = enviroment;
            this.mass = mass;
            this.radius = radius;
            this.Orbits = orbitsAround;

            this.habRating = UniqueRandom.Next(1, 10);
            this.rei = CalculateREI();
            this.initialBearing = UniqueRandom.Next(1, 12);

            InitialiseImage();
            MarkNew();
        }

        public SystemBody(Guid id)
        {
            dbSystemBody = new ESystemBody();

            if (id != Guid.Empty)
            {
                dbSystemBody = ESystemBody.LoadFromDB(id);
                Load();
            }
        }

        private SystemBody(ESystemBody entity)
        {
            this.dbSystemBody = entity;
            Load();
        }
        #endregion

        #region Initialise
        private void InitialiseImage()
        {
            switch (type)
            {
                case SystemBodyType.SUN:
                    {
                        this.ImageName = @"Generic Counters\Star.gif";
                        break;
                    }                    
                case SystemBodyType.PLANET:
                    {
                        switch (enviroment)
                        {
                            case SystemBodyEnviroment.TYPE_T:
                                this.ImageName = @"Generic Counters\Tplanet.gif";
                                break;
                            case SystemBodyEnviroment.TYPE_ST:
                                this.ImageName = @"Generic Counters\STplanet.gif";
                                break;
                            case SystemBodyEnviroment.TYPE_O1:
                            case SystemBodyEnviroment.TYPE_O2:
                            case SystemBodyEnviroment.TYPE_O3:
                                this.ImageName = @"Generic Counters\Oplanet.gif";
                                break;
                            case SystemBodyEnviroment.TYPE_V:
                                this.ImageName = @"Generic Counters\Vplanet.gif";
                                break;
                            case SystemBodyEnviroment.TYPE_G:
                                this.ImageName = @"Generic Counters\GasGiant.gif";
                                break;
                            case SystemBodyEnviroment.TYPE_I:
                                this.ImageName = @"Generic Counters\Oplanet.gif";
                                break;
                        }
                        break;
                    }
                case SystemBodyType.MOON:
                    {
                        this.ImageName = @"Generic Counters\Moon.gif";
                        break;
                    }
                case SystemBodyType.ASTEROID:
                    {
                        this.ImageName = @"Generic Counters\Asteriod.gif";
                        break;
                    }
            }
        }
        #endregion

        #region Public Static Properties
        static public List<SystemBody> SystemBodies
        {
            get
            {
                if (systemBodyList == null || systemBodyList.Count() == 0) FetchSystemBodies();
                return new List<SystemBody>(systemBodyList.Values);
            }
        }

        static public SystemBody GetSystemBody(Guid id)
        {
            if (systemBodyList == null || systemBodyList.Count() == 0) FetchSystemBodies();

            SystemBody result = systemBodyList.Values.FirstOrDefault(sSys => sSys.Id == id) as SystemBody;
            if (result != null)
                return result;

            return null;
        }

        static public SystemBody[] GetSystemBodies()
        {
            return SystemBodies.ToArray();
        }

        static public List<SystemBody> AllBodiesInSystem(Guid starSystemId, SystemBodyType typeToLocate)
        {
            if (systemBodyList == null || systemBodyList.Count() == 0) FetchSystemBodies();
            return systemBodyList.Values.Where(body => body.StarSystemId == starSystemId && body.Type == typeToLocate).ToList();
        }

        static public List<SystemBody> AllBodiesOrbiting(Guid systemBodyId, SystemBodyType typeToLocate)
        {
            if (systemBodyList == null || systemBodyList.Count() == 0) FetchSystemBodies();
            return systemBodyList.Values.Where(body => body.Orbits != null && body.Orbits.Id == systemBodyId && body.Type == typeToLocate).ToList();
        }
        #endregion

        #region Properties
        public Guid StarSystemId
        {
            get { return starSystemId; }
            private set { starSystemId = value; }
        }
        
        public SystemBodyType Type
        {
            get { return type; }
            protected set { type = value; }
        }

        public SystemBodyEnviroment Enviroment
        {
            get { return enviroment; }
            set
            {
                if (enviroment == value) return;
                enviroment = value;
                InitialiseImage();
                MarkDirty();
            }
        }

        public StarType StarType
        {
            get { return starType; }
            set
            {
                if (starType == value) return;
                starType = value;
                MarkDirty();
            }
        }

        public int Mass
        {
            get { return mass; }
            set
            {
                if (mass == value) return;
                mass = value;
                MarkDirty();
            }
        }

        public double REI
        {
            get { return rei; }
            set
            {
                if (rei == value) return;
                rei = value;
                MarkDirty();
            }
        }

        public int HabRating
        {
            get { return habRating; }
            set
            {
                if (habRating == value) return;
                habRating = value;
                MarkDirty();
            }
        }

        public int RadiusLM
        {
            get { return radius; }
            set
            {
                if (radius == value) return;
                radius = value;
                MarkDirty();
            }
        }

        public int InitialBearing
        {
            get { return initialBearing; }
            set
            {
                if (initialBearing == value) return;
                initialBearing = value;
                MarkDirty();
            }
        }

        public IEnumerable<SystemBody> Satalites
        {
            get
            {
                if( satalites.Count == 0 ) satalites.AddRange(SystemBody.AllBodiesOrbiting(Id, SystemBodyType.MOON));
                return satalites;
            }
            set { }
        }

        /// <summary>
        /// ONLY call the set property when deleting an orbit with null, setting is done through the constructor
        /// </summary>
        public SystemBody Orbits
        {
            get { return orbits; }
            set
            {
                if (orbits == value) return;
                if (value == null && orbits != null && orbits.satalites.Contains(this))
                    orbits.satalites.Remove(this);

                orbits = value;
                
                if (orbits != null && !orbits.satalites.Contains(this))
                    orbits.satalites.Add(this);

                MarkDirty();
            }
        }

        public Population Population
        {
            get { return population; }
            set
            {
                if (population == value) return;
                population = value;
                MarkDirty();
            }
        }
        #endregion

        #region Static Private Methods
        static private void FetchSystemBodies()
        {
            if (systemBodyList == null || systemBodyList.Count == 0)
            {
                systemBodyList = new Dictionary<Guid, SystemBody>();

                List<ESystemBody> entityList = ESystemBody.GetAll();
                lock (systemBodyList)
                {

                    //
                    foreach (ESystemBody entity in entityList)
                    {
                        SystemBody obj = new SystemBody(entity);
                        systemBodyList.Add(obj.Id, obj);
                    }

                    // Since Orbits relies on all system bodies being loaded before being sure all the bodies
                    // can be found, call CancelChanges() to force the Orbits to recheck for parents.
                    foreach (SystemBody body in systemBodyList.Values)
                        body.CancelChanges();
                }
            }
        }
        #endregion

        #region Public Methods
        public void ClearSatalites()
        {
            if (satalites.Count == 0) return;
            satalites.Clear();
            MarkDirty();
        }

        public SystemBodyCompatability Compatability(int raceHI, SystemBodyEnviroment perferedEnviroment)
        {
            SystemBodyCompatability compatibility = SystemBodyCompatability.HARSH;

            int minimumHI = raceHI - 2;
            int maximumHI = raceHI + 2;

            if (perferedEnviroment == SystemBodyEnviroment.TYPE_T)
            {
                if (enviroment == SystemBodyEnviroment.TYPE_ST)
                {
                    compatibility = SystemBodyCompatability.HOSTILE;
                }
                else if (enviroment == SystemBodyEnviroment.TYPE_T)
                {
                    if (minimumHI > 0 && maximumHI < 11)
                    {
                        if (habRating >= minimumHI && habRating <= maximumHI)
                            compatibility = SystemBodyCompatability.BENIGN;
                        else
                            compatibility = SystemBodyCompatability.HARSH;
                    }
                    else if (minimumHI < 1)
                    {
                        int modifiedHabRating = habRating - 10;
                        if (modifiedHabRating >= minimumHI && modifiedHabRating <= maximumHI)
                            compatibility = SystemBodyCompatability.BENIGN;
                        else
                            compatibility = SystemBodyCompatability.HARSH;
                    }
                    else if (maximumHI > 10)
                    {
                        int modifiedHabRating = habRating + 10;
                        if (modifiedHabRating >= minimumHI && modifiedHabRating <= maximumHI)
                            compatibility = SystemBodyCompatability.BENIGN;
                        else
                            compatibility = SystemBodyCompatability.HARSH;
                    }
                }
                else if (enviroment == SystemBodyEnviroment.TYPE_O2)
                {
                    compatibility = SystemBodyCompatability.DESOLATE;
                }
                else if (enviroment == SystemBodyEnviroment.TYPE_I)
                {
                    compatibility = SystemBodyCompatability.EXTREME;
                }
                else
                {
                    compatibility = SystemBodyCompatability.IMPOSSIBLE;
                }
            }
            else if (perferedEnviroment == SystemBodyEnviroment.TYPE_ST)
            {
                if (enviroment == SystemBodyEnviroment.TYPE_ST)
                {
                    compatibility = SystemBodyCompatability.BENIGN;
                }
                else if (enviroment == SystemBodyEnviroment.TYPE_T)
                {
                    if (minimumHI > 0 && maximumHI < 11)
                    {
                        if (habRating >= minimumHI && habRating <= maximumHI)
                            compatibility = SystemBodyCompatability.HARSH;
                        else
                            compatibility = SystemBodyCompatability.HOSTILE;
                    }
                    else if (minimumHI < 1)
                    {
                        int modifiedHabRating = habRating - 10;
                        if (modifiedHabRating >= minimumHI && modifiedHabRating <= maximumHI)
                            compatibility = SystemBodyCompatability.HARSH;
                        else
                            compatibility = SystemBodyCompatability.HOSTILE;
                    }
                    else if (maximumHI > 10)
                    {
                        int modifiedHabRating = habRating + 10;
                        if (modifiedHabRating >= minimumHI && modifiedHabRating <= maximumHI)
                            compatibility = SystemBodyCompatability.BENIGN;
                        else
                            compatibility = SystemBodyCompatability.HOSTILE;
                    }
                }
                else if (enviroment == SystemBodyEnviroment.TYPE_O2)
                {
                    compatibility = SystemBodyCompatability.DESOLATE;
                }
                else if (enviroment == SystemBodyEnviroment.TYPE_I)
                {
                    compatibility = SystemBodyCompatability.EXTREME;
                }
                else
                {
                    compatibility = SystemBodyCompatability.IMPOSSIBLE;
                }
            }

            return compatibility;
        }

        private double CalculateREI()
        {
            if (type == SystemBodyType.ASTEROID)
                return 1.05;

            int roll = UniqueRandom.Next(1, 10);
            if (enviroment == SystemBodyEnviroment.TYPE_I || enviroment == SystemBodyEnviroment.TYPE_O1 || enviroment == SystemBodyEnviroment.TYPE_O3)
                roll += 5;
            else if (enviroment == SystemBodyEnviroment.TYPE_O2)
                roll += 3;
            else if (enviroment == SystemBodyEnviroment.TYPE_G)
                roll += 1;

            if (roll <= 1)
            {
                return 0.6;
            }
            if (roll <= 3)
            {
                if (enviroment == SystemBodyEnviroment.TYPE_O1 || enviroment == SystemBodyEnviroment.TYPE_O3)
                    return 0.7;
                if (enviroment == SystemBodyEnviroment.TYPE_O2)
                    return 0.75;
                return 0.8;
            }
            if (roll <= 7)
            {
                if (enviroment == SystemBodyEnviroment.TYPE_O1 || enviroment == SystemBodyEnviroment.TYPE_O3)
                    return 0.8;
                if (enviroment == SystemBodyEnviroment.TYPE_O2)
                    return 0.9;
                return 1.0;
            }
            if (roll <= 9)
            {
                if (enviroment == SystemBodyEnviroment.TYPE_O1 || enviroment == SystemBodyEnviroment.TYPE_O3)
                    return 0.9;
                if (enviroment == SystemBodyEnviroment.TYPE_O2)
                    return 1.05;
                return 1.2;
            }

            if (enviroment == SystemBodyEnviroment.TYPE_O1 || enviroment == SystemBodyEnviroment.TYPE_O3)
                return 1.0;
            if (enviroment == SystemBodyEnviroment.TYPE_O2)
                return 1.2;
            return 1.4;
        }
        #endregion

        #region BaseObject overrides - Save, Update, etc
        public override void Save()
        {
            // Save links this is dependent on

            // Save this
            base.Save();

            // Save link dependent on this
        }

        internal override void Update()
        {
            if (MarkedToDelete)
            {
                // Delete dependencies

                DeleteNow();
            }
            else //if(IsNew || IsDirty)
            {
                if (IsNew)
                {
                    if (systemBodyList == null) systemBodyList = new Dictionary<Guid, SystemBody>();
                    systemBodyList.Add(this.Id, this);
                }

                SaveValues();
                dbSystemBody.SaveToDB(IsNew);
            }
        }

        internal override void DeleteNow()
        {
            if (dbSystemBody.Values == null)
                dbSystemBody.Values = new Stitch.Database.DBRow();

            dbSystemBody.Values["id"] = Id;

            dbSystemBody.Delete();

            //
            systemBodyList.Remove(Id);
            MarkDeleted();
        }

        internal override void SaveValues()
        {
            if (dbSystemBody.Values == null)
                dbSystemBody.Values = new Stitch.Database.DBRow();

            dbSystemBody.Values["id"] = Id;
            dbSystemBody.Values["imageName"] = ImageName;
            dbSystemBody.Values["heading"] = Heading;
            dbSystemBody.Values["mapId"] = MapPosition == null ? 0 : MapPosition.MapId;
            dbSystemBody.Values["row"] = MapPosition == null ? 0 : MapPosition.Row;
            dbSystemBody.Values["col"] = MapPosition == null ? 0 : MapPosition.Column;
            dbSystemBody.Values["ownerId"] = OwnerId;
            dbSystemBody.Values["starSystemId"] = StarSystemId;
            dbSystemBody.Values["type"] = EnumUtils.StringValueOf(this.Type);
            dbSystemBody.Values["enviroment"] = EnumUtils.StringValueOf(Enviroment);
            dbSystemBody.Values["starType"] = EnumUtils.StringValueOf(StarType);
            dbSystemBody.Values["mass"] = Mass;
            dbSystemBody.Values["rei"] = (decimal)REI;
            dbSystemBody.Values["habRating"] = HabRating;
            dbSystemBody.Values["radius"] = RadiusLM;
            dbSystemBody.Values["initialBearing"] = InitialBearing;
            dbSystemBody.Values["orbitsId"] = Orbits == null ? Guid.Empty : Orbits.Id;
        }

        internal override void LoadValues()
        {
            if (dbSystemBody.Values == null)
                return;

            Id = dbSystemBody.Values["id"];
            ImageName = dbSystemBody.Values["imageName"];
            Heading = dbSystemBody.Values["heading"];
            int mapid = dbSystemBody.Values["mapId"];
            if ( mapid != 0)
            {
                MapPosition = new Data.GameData.GameMap.MapIndex(dbSystemBody.Values["mapId"],
                                                                 dbSystemBody.Values["row"],
                                                                 dbSystemBody.Values["col"]);
            }
            OwnerId = dbSystemBody.Values["ownerId"];
            StarSystemId = dbSystemBody.Values["starSystemId"];
            this.Type = (SystemBodyType)EnumUtils.EnumValueOf(dbSystemBody.Values["type"], typeof(SystemBodyType));
            Enviroment = (SystemBodyEnviroment)EnumUtils.EnumValueOf(dbSystemBody.Values["enviroment"], typeof(SystemBodyEnviroment));
            StarType = (StarType)EnumUtils.EnumValueOf(dbSystemBody.Values["starType"], typeof(StarType));
            Mass = dbSystemBody.Values["mass"];
            REI = (double)dbSystemBody.Values["rei"];
            HabRating = dbSystemBody.Values["habRating"];
            RadiusLM = dbSystemBody.Values["radius"];
            InitialBearing = dbSystemBody.Values["initialBearing"];
            Orbits = systemBodyList.Values.FirstOrDefault(obj => obj.Id == dbSystemBody.Values["orbitsId"]);
            //dbSystemBody.Values["populationLinkId"];
        }

        internal override void InitialiseObject()
        {
        }
        #endregion

        #region ICloneable Members
        public override object Clone()
        {
            return base.Clone();
        }
        #endregion
    }
}
